﻿


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.IO;

namespace LAP
{
    /// <summary>
    /// zgke@sina.com
    /// 2008-09-26
    /// 磁盘扇区
    /// </summary>
    public partial class Disk
    {


        // 扇区操作方式
        public enum DISKMODE : uint
        {
            LOGIC = 0,  // 操作逻辑扇区
            PHYSIC = 1, // 操作物理扇区
        }

        // DeviceIoControl 函数输出类型
        struct STORAGE_DEVICE_NUMBER
        {
            ulong DeviceType;
            ulong DeviceNumber;
            ulong PartitionNumber;
        };


        private long _SectorLength = 0;     // 扇区数
        private int sectorSize = 512;       // 默认扇区大小为512字节
        private DISKMODE diskMode = DISKMODE.PHYSIC;    // 扇区操作模式(默认物理扇区)

        private SafeFileHandle tempHandle;
        private SafeFileHandle diskLogicHandler;    // 逻辑扇区
        private SafeFileHandle diskPhysisHandler;   // 物理扇区
        private string diskName = null;             // 盘符名称
        public string logText = "";                 // log 
        private bool available = false;             // 是否可用

        /// <summary>
        /// 扇区数
        /// </summary>
        public long SectorLength { get { return _SectorLength; } }

        // 设置扇区默认扇区大小 512字节对齐
        public void setSectorSize(int size) { sectorSize = (size / 512 + 1) * 512; }

        // 设置操作模式
        public void setDiskMode(DISKMODE mode) {
            diskMode = mode;
            if (diskMode == DISKMODE.LOGIC)
            {
                tempHandle = diskLogicHandler;
            }
            else
            {
                tempHandle = diskPhysisHandler;
            }
        }

        /*********************************************************************************
        ** 函数名称: DDR_USER_CFG_Add
        ** 功能描述: 打开指定盘符
        ** 输入参数: path: 盘符名称 .    
        ** 输出参数: None.
        ** 返回参数: None.
        **********************************************************************************/
        public Disk(string path)
        {
            if (path == null && path.Trim().Length == 0) return;

            path = path.Substring(0, path.IndexOf(":") + 1);
            diskName = "\\\\.\\" + path.Trim();
            try
            {
                Console.WriteLine(diskName);

                // 2.创建逻辑访问驱动
                diskLogicHandler = CreateFile("\\\\.\\" + path.Trim(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);

                // 1.创建物理访问驱动
                int devNum = HDVal(diskLogicHandler);                // 获取设备编号
                logText += @"\\\.\\PhysicalDrive" + devNum + "\r\n"; // 输出打印信息

                if (devNum < 2) goto err; // 容错判断,系统有两块硬盘(固态+机械)，故编号不能是0和1(防止破坏系统数据)
                diskPhysisHandler = CreateFile("\\\\.\\PhysicalDrive"+ devNum, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
            }
            catch (Exception ex)
            {
                logText += path + " Open Err!\r\n";
                goto err;
            }

            setDiskMode(DISKMODE.LOGIC);    // 设置访问模式
            GetSectorCount();               // 获取磁盘大小
            available = true;
            return;

        // 错误处理
        err:
            logText += "Disk arg err And not allowed to system disk！\r\n"; // 输出打印信息
            diskPhysisHandler = null;
            diskLogicHandler = null;
            tempHandle = null;
        }



        /// <summary>
        /// 获取扇区数
        /// </summary>
        private void GetSectorCount()
        {
            int lent = 0;
            byte[] ReturnByte = ReadSector(0);

            if (ReturnByte[0] == 0xEB && ReturnByte[1] == 0x58)           //DOS的好象都是32位
            {
                _SectorLength = (long)BitConverter.ToInt32(new byte[] { ReturnByte[32], ReturnByte[33], ReturnByte[34], ReturnByte[35] }, 0);
            }
            if (ReturnByte[0] == 0xEB && ReturnByte[1] == 0x52)          //NTFS好象是64位
            {
                _SectorLength = BitConverter.ToInt64(new byte[] { ReturnByte[40], ReturnByte[41], ReturnByte[42], ReturnByte[43], ReturnByte[44], ReturnByte[45], ReturnByte[46], ReturnByte[47] }, 0);
            }
        }


        /// <summary>
        /// 读一个扇区
        /// </summary>
        /// <param name="SectorIndex">扇区号</param>
        /// <returns>如果扇区数字大于分区信息的扇区数 返回NULL</returns>
        public byte[] ReadSector(int SectorIndex)
        {
            // 容错判断
            if (tempHandle==null ||SectorIndex > _SectorLength) return null;

            uint dwCB;
            byte[] ReturnByte = new byte[512];

            SetFilePointer(tempHandle, SectorIndex * 512, IntPtr.Zero, EMoveMethod.Begin);
            ReadFile(tempHandle, ReturnByte, 512, out dwCB, IntPtr.Zero);
            return ReturnByte;
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="SectorBytes">扇区 长度512</param>
        /// <param name="SectorIndex">扇区位置</param>
        public void WritSector(byte[] SectorBytes, int SectorIndex, int count)
        {
            if (tempHandle == null || SectorBytes.Length != 512) {
                //logText += "WritSector err！SectorIndex:" + SectorIndex + " SectorIndex:" + SectorIndex + " count:" + count + "\r\n";
                return;
            }
            //logText += "WritSector SectorIndex:" + SectorIndex + " count:" + count + "\r\n";
            uint dwCB;
            SetFilePointer(tempHandle, SectorIndex * 512, IntPtr.Zero, EMoveMethod.Begin);
            WriteFile(tempHandle, SectorBytes, 512, out dwCB, IntPtr.Zero);
        }


        // 获取磁盘编号
        private uint IOCTL_STORAGE_GET_DEVICE_NUMBER = 2953344;
        public int HDVal(SafeFileHandle diskLogicHandler)
        {
            int sdn = 0;
            int readed = 0;
            byte[] data = new byte[64];
            int result = DeviceIoControl(
                diskLogicHandler,                   // handle to device
                IOCTL_STORAGE_GET_DEVICE_NUMBER,    // dwIoControlCode
                IntPtr.Zero,                        // lpInBuffer
                0,                                  // nInBufferSize
                data,                               // output buffer
                data.Length * sizeof(ulong),        // size of output buffer
                ref readed,                         // number of bytes returne
                IntPtr.Zero                         // OVERLAPPED structure
             );
            return data[4];
        }



        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            diskPhysisHandler = null;
            diskLogicHandler = null;
            tempHandle = null;
            available = false;
        }

        public bool isAvailable() { return available; }

        /// <summary>
        /// 扇区显示转换
        /// </summary>
        /// <param name="SectorBytes">扇区 长度512</param>
        /// <returns>EB 52 90 ......55 AA</returns>
        public string GetString(byte[] SectorBytes)
        {
            if (SectorBytes.Length != 512) return "";
            StringBuilder ReturnText = new StringBuilder();

            int RowCount = 0;
            for (int i = 0; i != 512; i++)
            {
                ReturnText.Append(SectorBytes[i].ToString("X02") + " ");

                if (RowCount == 15)
                {
                    ReturnText.Append("\r\n");
                    RowCount = -1;
                }

                RowCount++;
            }
            return ReturnText.ToString();
        }

    }












    // 拆分Disk类 此处用于导入Win32 API
    partial  class Disk
    {
        private const uint GENERIC_READ = 0x80000000;
        private const uint GENERIC_WRITE = 0x40000000;

        private const uint FILE_SHARE_READ = 0x00000001;
        private const uint FILE_SHARE_WRITE = 0x00000002;

        private const uint OPEN_EXISTING = 3;
        public enum EMoveMethod : uint
        {
            Begin = 0,
            Current = 1,
            End = 2
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern SafeFileHandle CreateFileA(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);


        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern uint SetFilePointer([In] SafeFileHandle hFile, [In] int lDistanceToMove, IntPtr lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool ReadFile([In] SafeFileHandle hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

        [DllImport("kernel32.dll")]
        static extern bool WriteFile([In] SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);

        [DllImport("kernel32.dll")]
        private static extern int GetVolumeInformation(string lpRootPathName, string lpVolumeNameBuffer, int nVolumeNameSize,
            ref int lpVolumeSerialNumber, int lpMaximumComponentLength, int lpFileSystemFlags, string lpFileSystemNameBuffer, int nFileSystemNameSize);


        [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
        internal static extern int DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, byte[] lpOutbuffer, int nOutBufferSize, ref int lpByteReturned, IntPtr lpOverlapped);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

    }
}